/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 */

import * as os from 'os';

import { parseJsonFile, HvigorCoreNode, hvigorCore, HvigorTaskGroupType, DefaultTask, HvigorLogger } from '@ohos/hvigor';

import { buildOptionManager } from '../../project/build-option/build-mode-manager.js';

const log = HvigorLogger.getLogger('no-pattern-info');

/**
 * 通过执行hvigor 命令打印Hvigor build-profile的信息
 * 调用方式：hvigor buildInfo [-p module=<moduleName>] [-p buildOption] [-p json]
 *
 * @since 2024/11/29
 */
export class BuildInfo extends DefaultTask {
  constructor(hvigorNode: HvigorCoreNode) {
    super(hvigorNode, {
      name: 'buildInfo',
      group: HvigorTaskGroupType.HELP_TASK_GROUP,
      isEnabled: true,
      description: 'Displays the build information of the Project or Module.',
    });
  }

  registryAction(): Function {
    return (): void => {
      const properties = hvigorCore.getParameter().getExtParams();
      let buildInfo: any = {};
      let curNode = this.node;
      // 检查是否设置了displayOption
      const displayOptionFlag: boolean = properties.buildOption === '';
      // 检查是否设置了json
      const jsonFlag: boolean = properties.json === '';

      // 如果参数里设置了module，会计算指定module的buildInfo。否则计算项目级的buildInfo
      if (properties.module) {
        const module = this.node.findModuleByName(properties.module);
        if (module) {
          buildInfo = this.getModuleBuildInfo(module, displayOptionFlag);
          curNode = module;
        }
      } else {
        buildInfo = this.getProjectBuildInfo(curNode, displayOptionFlag);
      }

      // 如果有-p json参数，输出json格式。否则输出格式化的树状结构
      if (jsonFlag) {
        log.info(JSON.stringify(buildInfo, null, 2));
      } else {
        const curNodeName: string = curNode.getName();
        const outputTaskInfos: string[] = [
          '--------------------------------------------------------------------------',
          `All build information from hvigor node '${curNodeName}'`,
          '--------------------------------------------------------------------------',
        ];
        const formatInfo: string[] = [];
        this.createFormatInfoRecursively(buildInfo, formatInfo, 0);
        outputTaskInfos.push(...formatInfo);
        log.info(outputTaskInfos.join(os.EOL));
      }
    };
  }

  /**
   * 获取Module级构建信息
   * @return {any} 返回构建信息对象
   */
  private getModuleBuildInfo(module: HvigorCoreNode, displayBuildOption: boolean): any {
    const buildInfo: any = {};
    const buildProfile = parseJsonFile(module.getBuildProfilePath());
    const rootProfile = parseJsonFile(module.getProject().getBuildProfilePath());
    if (buildProfile) {
      const buildModes: string[] = this.getBuildModes(rootProfile);
      const buildOptions = this.getModuleBuildOption(buildProfile, buildModes, module.getName(), displayBuildOption);

      buildInfo.buildMode = buildModes;
      buildInfo.buildInfo = buildOptions;
    }
    return buildInfo;
  }

  /**
   * 获取Product级构建信息
   * @return {any} 返回构建信息对象
   */
  private getProjectBuildInfo(project: HvigorCoreNode, displayBuildOption: boolean): any {
    const buildInfo: any = {};
    const buildProfile = parseJsonFile(project.getBuildProfilePath());
    if (buildProfile) {
      const products: string[] = this.getProducts(buildProfile);
      const modules: string[] = this.getModules(buildProfile);
      const buildModes: string[] = this.getBuildModes(buildProfile);
      const buildOptions: any = this.getProductBuildOption(buildProfile, products, buildModes, displayBuildOption);
      buildInfo.product = products;
      buildInfo.module = modules;
      buildInfo.buildMode = buildModes;
      buildInfo.buildInfo = buildOptions;
    }
    return buildInfo;
  }

  /**
   * 递归创建格式化信息
   * @param buildInfo 任意类型的数据，可以是字符串、数组或对象
   * @param formatInfo 用于存储格式化信息的字符串数组
   * @param level 当前的层级，用于生成前缀
   */
  private createFormatInfoRecursively(buildInfo: any, formatInfo: string[], level: number): void {
    // 如果buildInfo是字符串，直接将其添加到formatInfo中，并添加相应的前缀
    if (typeof buildInfo === 'string' || typeof buildInfo === 'boolean') {
      formatInfo.push(this.getPrefix(level) + buildInfo);
      return;
    }
    // 如果buildInfo是数组，遍历数组中的每个元素，递归调用createFormatInfoRecursively
    if (buildInfo instanceof Array) {
      buildInfo.forEach((value) => {
        this.createFormatInfoRecursively(value, formatInfo, level);
      });
      return;
    }
    // 如果buildInfo是对象，遍历对象的每个键值对，将键添加到formatInfo中，并递归调用createFormatInfoRecursively处理值
    if (buildInfo instanceof Object) {
      for (const [key, value] of Object.entries(buildInfo)) {
        formatInfo.push(this.getPrefix(level) + key);
        this.createFormatInfoRecursively(value, formatInfo, level + 1);
      }
    }
  }

  /**
   * 根据给定的层级数生成相应的前缀字符串
   * @param level 层级数，用于决定生成的前缀字符串的长度
   * @return 返回生成的前缀字符串
   */
  private getPrefix(level: number): string {
    const PREFIX = '    ';
    let prefix = '';
    for (let i = 0; i < level; i++) {
      prefix += PREFIX;
    }
    return prefix;
  }

  /**
   * 获取产品列表
   * @param buildProfile 构建配置文件
   * @return 返回产品名称的字符串数组
   */
  private getProducts(buildProfile: any): string[] {
    const products: string[] = [];
    buildProfile?.app?.products &&
      buildProfile.app.products.forEach((product: any) => {
        products.push(product?.name);
      });
    return products;
  }

  /**
   * 获取产品列表
   * @param buildProfile 构建配置文件
   * @return 返回产品名称的字符串数组
   */
  private getModules(buildProfile: any): string[] {
    const modules: string[] = [];
    buildProfile?.modules &&
      buildProfile.modules.forEach((module: any) => {
        modules.push(module?.name);
      });
    return modules;
  }

  /**
   * 获取构建模式
   * @param {any} buildProfile - 构建配置文件
   * @return {string[]} 返回构建模式的数组
   */
  private getBuildModes(buildProfile: any): string[] {
    const buildModes: string[] = [];
    buildProfile?.app?.buildModeSet?.forEach((buildMode: any) => {
      buildModes.push(buildMode?.name);
    });
    return buildModes;
  }

  /**
   * 根据buildProfile内的信息，获取对应(moduleName, target, buildMode)元组的buildOption
   * @param buildProfile 构建配置
   * @param moduleName module名
   * @param buildModes 构建模式列表
   * @param displayBuildOption 是否加载buildOption
   * @return 构建选项对象
   */
  private getModuleBuildOption(buildProfile: any, buildModes: string[], moduleName: string, displayBuildOption: boolean): any {
    const buildOptions: any = {};
    buildProfile?.targets.forEach((target: any) => {
      buildModes.forEach((buildMode) => {
        const targetName = target?.name;
        if (!(targetName in buildOptions)) {
          buildOptions[targetName] = {};
        }
        buildOptions[targetName][buildMode] = displayBuildOption
          ? buildOptionManager.getTargetBuildOptionWithProductBuildMode(moduleName, targetName, buildMode)
          : {};
      });
    });
    return buildOptions;
  }

  /**
   * 根据buildProfile内的信息，获取对应(product, target, buildMode)元组的buildOption
   * @param buildProfile 构建配置
   * @param products 产品列表
   * @param buildModes 构建模式列表
   * @param displayBuildOption 是否加载buildOption
   * @return 构建选项对象
   */
  private getProductBuildOption(buildProfile: any, products: string[], buildModes: string[], displayBuildOption: boolean): any {
    const buildOptions: any = {};
    products.forEach((product) => {
      buildOptions[product] = {};
    });

    const modules: any[] = buildProfile.modules;
    modules?.forEach((module: any) => {
      module?.targets?.forEach((target: any) => {
        target?.applyToProducts?.forEach((applyProduct: string) => {
          buildModes.forEach((buildMode: string) => {
            // 构建productName--moduleName--targetName--buildMode--buildOptions树状图
            const productName = applyProduct;
            const moduleName = module?.name;
            const targetName = target?.name;
            if (!(moduleName in buildOptions[productName])) {
              buildOptions[productName][moduleName] = {};
            }
            if (!(targetName in buildOptions[productName][moduleName])) {
              buildOptions[productName][moduleName][targetName] = {};
            }
            buildOptions[productName][moduleName][targetName][buildMode] = displayBuildOption
              ? buildOptionManager.getTargetBuildOptionWithProductBuildMode(moduleName, targetName, buildMode, productName)
              : {};
          });
        });
      });
    });
    return buildOptions;
  }
}
